from typing import DefaultDict
from django.db import models
import datetime
import numpy as np
# datetime.datetime().timestamp
# Create your models here.


class CellGroup(models.Model):
    name = models.CharField(verbose_name='Cell Group', max_length=12, unique=True)
    # soc_max = models.FloatField(verbose_name='SOC Max')
    # soc_min = models.FloatField(verbose_name='SOC min')
   

class Cell(models.Model):
    name = models.CharField(verbose_name='cell name', max_length=12, unique=True)
    group = models.ForeignKey(to=CellGroup, on_delete=models.SET_NULL, null=True)

    def __str__(self) -> str:
        if self.group is not None:
            return self.name + ', group:' + self.group.name
        else:
            return self.name

class CellGroupLogs(models.Model):
    soc_range = models.FloatField(verbose_name='SOC Range')
    temp_range = models.FloatField(verbose_name='Temp Range')
    v_range = models.FloatField(verbose_name='V Range')
    v_std = models.FloatField(verbose_name='V Std')
    group = models.ForeignKey(to=CellGroup, on_delete=models.CASCADE)
    t = models.IntegerField(verbose_name='Time')
    
    @staticmethod
    def update_all():
        for g in CellGroup.objects.all():
            try:
                CellGroupLogs.update_fields(g.name)
            except Exception as e:
                print('Waraing: Update ' +  g.name + ' Failed!')
                print(e)

    @staticmethod
    def update_fields(g):
        g = CellGroup.objects.get(name = g)
        cells = Cell.objects.filter(group = g)
        tdata = DefaultDict(list)
        vdata = DefaultDict(list)
        socdata = DefaultDict(list)
        tset = set()
        for c in cells:
            logs = Logs.objects.filter(cell = c).all()
            for l in logs:
                tdata[l.t].append(l.temp)
                vdata[l.t].append(l.v)
                socdata[l.t].append(l.soc)
                tset.add(l.t)
        for t in tset:
            gl = CellGroupLogs.objects.get_or_create(group = g, t = t)[0]
            tarr = np.array(tdata[t])
            varr = np.array(vdata[t])
            socarr = np.array(socdata[t])
            gl.temp_range = tarr.max() - tarr.min()
            gl.v_range = varr.max() - varr.min()
            gl.v_std = varr.std()
            gl.soc_range = socarr.max() - socarr.min()
            gl.t = t
            gl.save()

class Logs(models.Model):
    cell = models.ForeignKey(to=Cell, on_delete=models.CASCADE)
    temp = models.FloatField(verbose_name='Temp')
    rul = models.FloatField(verbose_name='RUL')
    soc = models.FloatField(verbose_name='soc')
    soh = models.FloatField(verbose_name='SOH')
    v = models.FloatField(verbose_name='Voltage')
    i = models.FloatField(verbose_name='current')
    t = models.IntegerField(verbose_name='Time')

    def __str__(self):
        return f'{str(self.cell)},{self.t}'

    def to_json(self):
        res = {}
        for k in self.all_keys:
            res[k] = self[k]
        res['cell'] = self.cell.name
        return res

    @property
    def all_keys(self):
        return self.origin_keys + self.calc_keys

    @property
    def origin_keys(self):
        return ['t', 'temp', 'v', 'i', ]

    @property
    def calc_keys(self):
        return ['soc', 'soh', 'rul']

    @staticmethod
    def calc_fields():
        pass

    def from_json(self, obj:dict):
        for k in obj.keys():
            if k in self.origin_keys:
                self[k] = obj[k]
        


class Tasks(models.Model):
    task = models.CharField(max_length=64, verbose_name='Task ID', unique=True)
    state = models.IntegerField(verbose_name='Task Interge')
    msg = models.CharField(max_length=128, verbose_name='Task detail')
    start = models.DateTimeField(verbose_name='Start Time', auto_now=False, null=True)
    end = models.DateTimeField(verbose_name='End Time', auto_now=False, null=True)

    def __str__(self) -> str:
        return self.task + ':' + self.msg[:12]

    @property
    def duration(self):
        if self.start is not None and self.end is not None:
            return self.end.timestamp() - self.start.timestamp()
        if self.start is not None:
            return datetime.datetime.now().timestamp() - self.start.timestamp()
        return 0